import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.util.ArrayList;

public class CADUEventProcessor extends DrawObjectEventProcessor
{
    private ArrayList<DrawObject> ClearDrawObjectArrayList(ArrayList<DrawObject> DrawObjectArrayList)
    {
        for (int m = 0; m < DrawObjectArrayList.size(); m++)
            DrawObjectArrayList.set(m, null);
        DrawObjectArrayList = null;
        System.gc();
        return DrawObjectArrayList;
    }

    private ArrayList<DrawObject> CreateCADU(Point Pos, int AxonCount, int DendritesCount, int[] AllowedDelaysInTicks, int LearningNeuronCount)
    { // returns all DrawObjects the CADU consists of or null in case of an error (currently if out of heap space)

        ArrayList<DrawObject> CADUObjects = new ArrayList<DrawObject>();

        int GridSizex = 50; // TODO: don't hard code
        int GridSizey = 50; // TODO: don't hard code

        int AllowedDelayInTicksMax = 0;

        for (int m = 0; m < AllowedDelaysInTicks.length; m++)
            if (AllowedDelaysInTicks[m] > AllowedDelayInTicksMax)
                AllowedDelayInTicksMax = AllowedDelaysInTicks[m];

        Pos.x += GridSizex * 1; // simple correction to compensate non-optimal calculations in following code
        Pos.y += GridSizey * 3; // simple correction to compensate non-optimal calculations in following code

        int CADUWidth = 0;
        int CADUHeight = 0;

        Rectangle[] InterfaceCommentFieldRectangles = new Rectangle[4];

        for (int i = 0; i < InterfaceCommentFieldRectangles.length; i++)
            InterfaceCommentFieldRectangles[i] = new Rectangle();

        // create Nodes of vertical (axon) pathway...
        // ooo <--
        // |||
        // ooo <--
        // |||
        // ooo <--

        Node[] VerticalNodes = new Node[AxonCount * (AllowedDelayInTicksMax + 1)];

        for (int x = 0; x < AxonCount; x++)
        {
            for (int y = 0; y <= AllowedDelayInTicksMax; y++)
            {
                int NodePosx = Pos.x + x * GridSizex;
                int NodePosy = Pos.y + y * GridSizey;

                VerticalNodes[y * AxonCount + x] = new Node(new Point(NodePosx, NodePosy));

                CADUObjects.add(VerticalNodes[y * AxonCount + x]);

                CADUWidth = Math.max(CADUWidth, NodePosx - Pos.x);
                CADUHeight = Math.max(CADUHeight, NodePosy - Pos.y);

                if (x == 0 && y == 0)
                {
                    InterfaceCommentFieldRectangles[1].x = NodePosx - GridSizex;
                    InterfaceCommentFieldRectangles[1].y = NodePosy - GridSizey * 2;
                }
                if (x == AxonCount - 1 && y == 0)
                {
                    InterfaceCommentFieldRectangles[1].width = NodePosx + GridSizex - InterfaceCommentFieldRectangles[1].x;
                    InterfaceCommentFieldRectangles[1].height = GridSizey * 3;
                }
            }
        }

        // connect Nodes top down...
        // ooo
        // ||| <--
        // ooo
        // ||| <--
        // ooo

        for (int x = 0; x < AxonCount; x++)
        {
            for (int y = 1; y <= AllowedDelayInTicksMax; y++)
            {
                Line Line = new Line(VerticalNodes[(y - 1) * AxonCount + x], VerticalNodes[y * AxonCount + x]);

                CADUObjects.add(Line);
            }
        }

        // create LearningNeurons and their "interface" Nodes...

        // LearningNeurons can optionally be used to "filter" output of
        // the normal CADU Neuron array - LearningNeuron only fires
        // if a specific normal Neuron fires.

        int NeuronCount = (int) Math.pow(AllowedDelaysInTicks.length, DendritesCount);

        Neuron[] LearningNeurons = new Neuron[LearningNeuronCount];

        for (int n = 0; n < LearningNeuronCount; n++)
        {
            // create actual LearningNeuron

            int NeuronSizey = 100; // TODO: don't hard code
            int NeuronPosyMax = Pos.y + GridSizey * 2 + (NeuronCount - 1) * (NeuronSizey + GridSizey); // (NeuronCount - 1) is the maximal "n" used when placing normal Neurons (see below)

            int NodePosyMax = Pos.y + AllowedDelayInTicksMax * GridSizey; // position of the bottom-most Nodes from the vertical pathway

            int LearningNeuronSizex = 100; // TODO: don't hard code
            int LearningNeuronPosy = Math.max(NeuronPosyMax, NodePosyMax) + NeuronSizey + GridSizey;

            Neuron LearningNeuron = new Neuron(new Point(Pos.x + n * (LearningNeuronSizex + GridSizex), LearningNeuronPosy));
            LearningNeuron.SetIsLearningNeuron(true);
            LearningNeuron.AddProperty(DrawObject.PROPERTY_DONT_DRAW_IN_REDUCED_DRAWING_MODE);

            CADUWidth = Math.max(CADUWidth, (LearningNeuron.Pos.x + LearningNeuron.Size.x) - Pos.x);
            CADUHeight = Math.max(CADUHeight, (LearningNeuron.Pos.y + LearningNeuron.Size.y) - Pos.y);

            NeuronOutputNode LearningNeuronOutputNode = LearningNeuron.AddOutputNode();
            LearningNeuronOutputNode.AddProperty(DrawObject.PROPERTY_DONT_DRAW_IN_REDUCED_DRAWING_MODE);

            Neuron.NeuronInputDrawObjects LearningNeuronInputDrawObjects = LearningNeuron.AddInputFieldAndNode();
            LearningNeuronInputDrawObjects.GetField().ToggleIsNegative(); // this is the NEGATIVE input, normal inputs are added below!
            LearningNeuronInputDrawObjects.GetField().AddProperty(DrawObject.PROPERTY_DONT_DRAW_IN_REDUCED_DRAWING_MODE);
            LearningNeuronInputDrawObjects.GetNode().AddProperty(DrawObject.PROPERTY_DONT_DRAW_IN_REDUCED_DRAWING_MODE);

            CADUObjects.add(LearningNeuron);
            CADUObjects.add(LearningNeuronOutputNode);
            CADUObjects.add(LearningNeuronInputDrawObjects.GetField()); // add field ALWAYS before node!
            CADUObjects.add(LearningNeuronInputDrawObjects.GetNode());

            LearningNeurons[n] = LearningNeuron;

            // create connection Lines from/to interface Nodes...

            // from interface Node to LearningNeuron's negative input
            int LearningNeuronNegativeInputInterfaceNodex = Pos.x + (AxonCount * GridSizex) + GridSizex + (n * GridSizex);

            Node LearningNeuronNegativeInputInterfaceNode = new Node(new Point(LearningNeuronNegativeInputInterfaceNodex, Pos.y));
            Line LearningNeuronNegativeInputInterfaceLine = new Line(LearningNeuronNegativeInputInterfaceNode, LearningNeuronInputDrawObjects.GetNode());
            LearningNeuronNegativeInputInterfaceLine.AddProperty(DrawObject.PROPERTY_DONT_DRAW_IN_REDUCED_DRAWING_MODE);

            CADUObjects.add(LearningNeuronNegativeInputInterfaceNode);
            CADUObjects.add(LearningNeuronNegativeInputInterfaceLine);

            if (n == 0)
            {
                InterfaceCommentFieldRectangles[2].x = LearningNeuronNegativeInputInterfaceNodex - GridSizex;
                InterfaceCommentFieldRectangles[2].y = Pos.y - GridSizey * 2;
            }
            if (n == LearningNeuronCount - 1)
            {
                InterfaceCommentFieldRectangles[2].width = LearningNeuronNegativeInputInterfaceNodex + GridSizex * 3 - InterfaceCommentFieldRectangles[2].x;
                InterfaceCommentFieldRectangles[2].height = GridSizey * 3;
            }

            // from LearningNeuron output to interface Node
            int InterfaceOutputNodex = Pos.x + (AxonCount * GridSizex) + GridSizex + (LearningNeuronCount * GridSizex) + GridSizex * 3 + (n * GridSizex);

            Node LearningNeuronOutputInterfaceNode = new Node(new Point(InterfaceOutputNodex, Pos.y));
            Line LearningNeuronOutputInterfaceLine = new Line(LearningNeuronOutputNode, LearningNeuronOutputInterfaceNode);
            LearningNeuronOutputInterfaceLine.AddProperty(DrawObject.PROPERTY_DONT_DRAW_IN_REDUCED_DRAWING_MODE);

            CADUObjects.add(LearningNeuronOutputInterfaceNode);
            CADUObjects.add(LearningNeuronOutputInterfaceLine);

            if (n == 0)
            {
                InterfaceCommentFieldRectangles[3].x = InterfaceOutputNodex - GridSizex;
                InterfaceCommentFieldRectangles[3].y = Pos.y - GridSizey * 2;
            }
            if (n == LearningNeuronCount - 1)
            {
                InterfaceCommentFieldRectangles[3].width = InterfaceOutputNodex + GridSizex * 3 - InterfaceCommentFieldRectangles[3].x;
                InterfaceCommentFieldRectangles[3].height = GridSizey * 3;
            }
        }

        // create normal Neurons and their Line connections...

        int LoopCount = (int) Math.pow(2.0, AxonCount);
        int AxonUsedSetIndex = 0;
        int CheckHeapSizeStep = 100;

        for (int AxonUsedBits = 0; AxonUsedBits < LoopCount; AxonUsedBits++)
        {
            int BitSetCount = 0;
            for (int m = 0; m < AxonCount; m++)
                if ((AxonUsedBits & (1 << m)) != 0) // by checking bits, you can get any on/off combination possible
                    BitSetCount++;

            if (BitSetCount != DendritesCount) // every Neuron is connected with one possible combination of vertical pathway Nodes (some Nodes might not be reached, their only task is to delay excitement that's traveling through the vertical pathway)
                continue;

            // determine which vertical pathway "lines" are to be used...
            // ooo
            // |||
            // ooo
            // |||
            // ooo
            // ^ ^
            // | |

            boolean[] AxonUsed = new boolean[AxonCount];
            for (int m = 0; m < AxonCount; m++)
                if ((AxonUsedBits & (1 << m)) != 0)
                    AxonUsed[m] = true;
                else
                    AxonUsed[m] = false;

            // now create "normal" Neurons...

            Neuron[] Neurons = new Neuron[NeuronCount];

            int NeuronSizex = 100; // TODO: don't hard code
            int NeuronSizey = 100; // TODO: don't hard code

            for (int n = 0; n < NeuronCount; n++)
            {
                if ((n % CheckHeapSizeStep) == 0)
                    if (!(Tools.CheckHeapSize()))
                        return ClearDrawObjectArrayList(CADUObjects);

                // create normal Neuron
                int NeuronPosx = Pos.x + AxonCount * GridSizex + GridSizex + AxonUsedSetIndex * NeuronSizex * 2;
                int NeuronPosy = Pos.y + GridSizey * 2 + n * (NeuronSizey + GridSizey);

                Neurons[n] = new Neuron(
                    new Point(
                        NeuronPosx,
                        NeuronPosy
                    )
                );
                Neurons[n].AddProperty(DrawObject.PROPERTY_DONT_DRAW_IN_REDUCED_DRAWING_MODE);

                NeuronOutputNode NeuronOutputNode = Neurons[n].AddOutputNode();
                NeuronOutputNode.AddProperty(DrawObject.PROPERTY_DONT_DRAW_IN_REDUCED_DRAWING_MODE);

                CADUObjects.add(Neurons[n]);
                CADUObjects.add(NeuronOutputNode);

                CADUWidth = Math.max(CADUWidth, (Neurons[n].Pos.x + Neurons[n].Size.x) - Pos.x);
                CADUHeight = Math.max(CADUHeight, (Neurons[n].Pos.y + Neurons[n].Size.y) - Pos.y);

                // connect normal Neuron output with a newly created input of each LearningNeuron
                for (int l = 0; l < LearningNeuronCount; l++)
                {
                    Neuron.NeuronInputDrawObjects LearningNeuronInputDrawObjects = LearningNeurons[l].AddInputFieldAndNode();

                    LearningNeuronInputDrawObjects.GetField().AddProperty(DrawObject.PROPERTY_DONT_DRAW_IN_REDUCED_DRAWING_MODE);
                    LearningNeuronInputDrawObjects.GetNode().AddProperty(DrawObject.PROPERTY_DONT_DRAW_IN_REDUCED_DRAWING_MODE);

                    Line Line = new Line(NeuronOutputNode, LearningNeuronInputDrawObjects.GetNode());
                    Line.AddProperty(DrawObject.PROPERTY_DONT_DRAW_IN_REDUCED_DRAWING_MODE);

                    CADUObjects.add(LearningNeuronInputDrawObjects.GetField());
                    CADUObjects.add(LearningNeuronInputDrawObjects.GetNode());
                    CADUObjects.add(Line);

                    CADUWidth = Math.max(CADUWidth, (LearningNeurons[l].Pos.x + LearningNeurons[l].Size.x) - Pos.x);
                    CADUHeight = Math.max(CADUHeight, (LearningNeurons[l].Pos.y + LearningNeurons[l].Size.y) - Pos.y);
                }
            }

            int[] AxonNodeIndex = new int[AxonCount];

            int ConnectionNeuronIndex = 0;
            boolean ConnectionFinished = false;

            while (!(ConnectionFinished))
            {
                for (int x = 0; x < AxonCount; x++)
                {
                    if (AxonUsed[x])
                    {
                        if ((x % CheckHeapSizeStep) == 0)
                            if (!(Tools.CheckHeapSize()))
                                return ClearDrawObjectArrayList(CADUObjects);

                        Node ConnectionNode = VerticalNodes[AllowedDelaysInTicks[AxonNodeIndex[x]] * AxonCount + x];
                        Neuron ConnectionNeuron = Neurons[ConnectionNeuronIndex];

                        Neuron.NeuronInputDrawObjects ConnectionNeuronInputFieldAndNode = ConnectionNeuron.AddInputFieldAndNode();
                        ConnectionNeuronInputFieldAndNode.GetField().SetStrength((int) Math.ceil(ConnectionNeuron.GetStrengthMax() / (double) DendritesCount));
                        ConnectionNeuronInputFieldAndNode.GetField().AddProperty(DrawObject.PROPERTY_DONT_DRAW_IN_REDUCED_DRAWING_MODE);
                        ConnectionNeuronInputFieldAndNode.GetNode().AddProperty(DrawObject.PROPERTY_DONT_DRAW_IN_REDUCED_DRAWING_MODE);

                        CADUObjects.add(ConnectionNeuronInputFieldAndNode.GetField());
                        CADUObjects.add(ConnectionNeuronInputFieldAndNode.GetNode());

                        Line ConnectionLine = new Line(ConnectionNode, ConnectionNeuronInputFieldAndNode.GetNode());
                        ConnectionLine.AddProperty(DrawObject.PROPERTY_DONT_DRAW_IN_REDUCED_DRAWING_MODE);

                        CADUObjects.add(ConnectionLine);
                    }
                }

                // define next combination of Nodes on the vertical pathway...

                // NOTE: on the right-most axon pathway line, we try to connect at the next Node below,
                // if there is none, connect to the top-most again and check the further left
                // vertical pathway line in the same way; only check those vertical pathways lines
                // which belong to the current x-on/off combination...

                for (int m = AxonCount - 1; m >= 0; m--)
                {
                    if (AxonUsed[m])
                    {
                        AxonNodeIndex[m]++;
                        if (AxonNodeIndex[m] >= AllowedDelaysInTicks.length)
                        {
                            AxonNodeIndex[m] = 0;

                            boolean AxonUsedBefore = false;
                            for (int n = m - 1; n >= 0; n--)
                                if (AxonUsed[n])
                                    AxonUsedBefore = true;

                            if (!(AxonUsedBefore))
                            {
                                ConnectionFinished = true;
                                break;
                            }
                        }
                        else
                            break;
                    }
                }

                ConnectionNeuronIndex++;
            }

            AxonUsedSetIndex++;
        }

        // create a CommentField which serves as a "Frame" around the CADU,
        // as a visual help for the user (user can also name the CADU by
        // setting CommentField's TextField text)...

        InterfaceCommentFieldRectangles[0].x = Pos.x - GridSizex * 1;
        InterfaceCommentFieldRectangles[0].y = Pos.y - GridSizey * 3;
        InterfaceCommentFieldRectangles[0].width = CADUWidth + GridSizex * 2;
        InterfaceCommentFieldRectangles[0].height = CADUHeight + GridSizey * 4;

        for (int i = 0; i < (LearningNeuronCount == 0 ? 1 : InterfaceCommentFieldRectangles.length); i++) // if no LearningNeurons, just draw the Frame around the complete CADU
        { // ^mind draw order, so that big CADU's Frame Lines do not cover interface Frames' TextFields

            CommentField CommentField = new CommentField(
                new Point(InterfaceCommentFieldRectangles[i].x, InterfaceCommentFieldRectangles[i].y),
                new Point(InterfaceCommentFieldRectangles[i].width, InterfaceCommentFieldRectangles[i].height)
            );

            CADUObjects.add(CommentField);

            ArrayList<DrawObject> CommentFieldDrawObjects = CommentField.GenerateChildObjects();

            for (int m = 0; m < CommentFieldDrawObjects.size(); m++)
            {
                if (!(CommentFieldDrawObjects.get(m) instanceof TextField))
                    CommentFieldDrawObjects.get(m).AddProperty(DrawObject.PROPERTY_DONT_DRAW_IN_REDUCED_DRAWING_MODE);
                else
                {
                    // if (i == 0) // user shall name this properly
                    // ((TextField) CommentFieldDrawObjects.get(m)).SetText("");
                    if (i == 1)
                        ((TextField) CommentFieldDrawObjects.get(m)).SetText("always if this");
                    if (i == 2)
                        ((TextField) CommentFieldDrawObjects.get(m)).SetText("and once this");
                    if (i == 3)
                        ((TextField) CommentFieldDrawObjects.get(m)).SetText("then always this");
                }
                CADUObjects.add(CommentFieldDrawObjects.get(m));
            }
        }

        // finished, return all created DrawObjects, caller must add them to ObjectStore
        return CADUObjects;
    }

    @Override
    public void TryProcessUserKeyboardAction(MainWindow.GUIElements GUIElements, KeyEventProcessor KeyEventProcessor, MouseEventProcessor MouseEventProcessor, ObjectStorage ObjectStorage, IOProgram IOProgram)
    {
        if (!(KeyboardEventsToBeProcessed(GUIElements)))
            return;

        // ****************************************************************
        // u (create CADU)
        if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_U) &&
            KeyEventProcessor.IsControlPressed())
        {
            boolean JavaCADUOrNeuronCADU = Tools.ShowYesNoDialog(
                GUIElements.GetMainWindow(),
                "Please choose:\r\n" +
                    "\r\n" +
                    "YES: create JavaCADU (hard-coded; internal working mechanisms not visible, but fast and RAM-efficient)\r\n" +
                    "NO : create CADU consisting of single objects (Nodes, Lines, Neurons - descriptive, but (very) large, slow, memory-consuming)\r\n" +
                    "\r\n" +
                    "(choose NO for learning purposes, YES for practical use)..."
            );

            if (JavaCADUOrNeuronCADU == true)
            {
                // JavaCADU
                String AxonCountString = Tools.ShowInputDialog("ChaoticalANDDetectionUnit: Axon count?\r\n(how many pixels or words exist)");
                if (AxonCountString == null || AxonCountString.length() < 1)
                    return;

                int AxonCount = Integer.parseInt(AxonCountString);
                if (AxonCount < 1 || AxonCount > 100)
                {
                    Tools.ShowMessageDialog(null, "AxonCount must be between 1 and 100!\r\nCombine CADU units if necessary (output of one is input of another).");
                    return;
                }

                String DendritesCountString = Tools.ShowInputDialog("ChaoticalANDDetectionUnit: Dendrites count?\r\n(how many pixels or words required to make Neuron fire)");
                if (DendritesCountString == null || DendritesCountString.length() < 1)
                    return;

                int DendritesCount = Integer.parseInt(DendritesCountString);
                if (DendritesCount < 1 || DendritesCount > AxonCount)
                {
                    Tools.ShowMessageDialog(null, "DendritesCount must be between 1 and " + AxonCount + " (previously entered AxonCount)!");
                    return;
                }

                String SuccessionLengthString = Tools
                    .ShowInputDialog("ChaoticalANDDetectionUnit: Succession length?\r\n(regards time, e.g. how many *additional*, consecutive IO program lines involved;\r\nuse 0 to recognize static patterns, like VisualField images)");
                if (SuccessionLengthString == null || SuccessionLengthString.length() < 1)
                    return;

                int SuccessionLength = Integer.parseInt(SuccessionLengthString);
                if (SuccessionLength < 0 || SuccessionLength > 100)
                {
                    Tools.ShowMessageDialog(null, "SuccessionLength must be between 0 (only detect combinations) and 100!");
                    return;
                }

                String LearningNeuronCountString = Tools.ShowInputDialog(
                    "ChaoticalANDDetectionUnit: LearningNeuronCount count?\r\n(how many input patterns are to be each associated (AND-ed) with an additional excitement;\r\nserves as a kind of \"filter\": e.g. only those VisualField patterns are of interest\r\nwhich appear at the same time with an excited TextField containing \"this is a flower\")"
                );
                if (LearningNeuronCountString == null || LearningNeuronCountString.length() < 1)
                    return;

                int LearningNeuronCount = Integer.parseInt(LearningNeuronCountString);
                if (LearningNeuronCount < 0 || LearningNeuronCount > 100)
                {
                    Tools.ShowMessageDialog(null, "LearningNeuronCount must be between 0 and 100!");
                    return;
                }

                int[] AllowedDelaysInTicks = new int[SuccessionLength + 1];

                for (int m = 0; m <= SuccessionLength; m++)
                    AllowedDelaysInTicks[m] = m * Simulation.TicksPerIOTextAreaLine();

                ObjectStorage.AddObject(
                    "JavaCADU",
                    (Object) DrawObject.AlignOnGrid(DrawObject.UnZoomPoint(MouseEventProcessor.GetMousePosPoint(GUIElements.GetMainWindow()))),
                    (Object) (new JavaCADU.JavaCADUParameterSet(AxonCount, DendritesCount, AllowedDelaysInTicks, LearningNeuronCount))
                );

                GUIElements.GetMainWindow().DoRedraw();
            }
            else
            {
                // composed CADU (only choice in Beta 2)
                String AxonCountString = Tools.ShowInputDialog("ChaoticalANDDetectionUnit: Axon count?\r\n(how many pixels or words exist;\r\nmind that more than 10 could be (very) resource-consuming)");
                if (AxonCountString == null || AxonCountString.length() < 1)
                    return;

                int AxonCount = Integer.parseInt(AxonCountString);
                if (AxonCount < 1 || AxonCount > 50)
                {
                    Tools.ShowMessageDialog(null, "AxonCount must be between 1 and 50!\r\nCombine CADU units if necessary (output of one is input of another).");
                    return;
                }

                String DendritesCountString = Tools.ShowInputDialog("ChaoticalANDDetectionUnit: Dendrites count?\r\n(how many pixels or words required to make Neuron fire)");
                if (DendritesCountString == null || DendritesCountString.length() < 1)
                    return;

                int DendritesCount = Integer.parseInt(DendritesCountString);
                if (DendritesCount < 1 || DendritesCount > AxonCount)
                {
                    Tools.ShowMessageDialog(null, "DendritesCount must be between 1 and " + AxonCount + " (previously entered AxonCount)!");
                    return;
                }

                String SuccessionLengthString = Tools
                    .ShowInputDialog("ChaoticalANDDetectionUnit: Succession length?\r\n(regards time, e.g. how many *additional*, consecutive IO program lines involved;\r\nuse 0 to recognize static patterns, like VisualField images)");
                if (SuccessionLengthString == null || SuccessionLengthString.length() < 1)
                    return;

                int SuccessionLength = Integer.parseInt(SuccessionLengthString);
                if (SuccessionLength < 0 || SuccessionLength > 5)
                {
                    Tools.ShowMessageDialog(null, "SuccessionLength must be between 0 (only detect combinations) and 5!");
                    return;
                }

                String LearningNeuronCountString = Tools.ShowInputDialog(
                    "ChaoticalANDDetectionUnit: LearningNeuronCount count?\r\n(how many input patterns are to be each associated (AND-ed) with an additional excitement;\r\nserves as a kind of \"filter\": e.g. only those VisualField patterns are of interest\r\nwhich appear at the same time with an excited TextField containing \"this is a flower\")"
                );
                if (LearningNeuronCountString == null || LearningNeuronCountString.length() < 1)
                    return;

                int LearningNeuronCount = Integer.parseInt(LearningNeuronCountString);
                if (LearningNeuronCount < 0 || LearningNeuronCount > 50)
                {
                    Tools.ShowMessageDialog(null, "LearningNeuronCount must be between 0 and 50!");
                    return;
                }

                int[] AllowedDelaysInTicks = new int[SuccessionLength + 1];

                for (int m = 0; m <= SuccessionLength; m++)
                    AllowedDelaysInTicks[m] = m * Simulation.TicksPerIOTextAreaLine();

                final String GeneratingMessage = "Generating CADU, please wait...";

                GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText(GeneratingMessage, false, true, true, true);

                ArrayList<DrawObject> Objects = CreateCADU(
                    DrawObject.AlignOnGrid(DrawObject.UnZoomPoint(MouseEventProcessor.GetMousePosPoint(GUIElements.GetMainWindow()))),
                    AxonCount, DendritesCount, AllowedDelaysInTicks, LearningNeuronCount
                );

                if (Objects != null)
                {
                    int NeuronCount = 0;
                    for (int m = 0; m < Objects.size(); m++)
                    {
                        ObjectStorage.AddObjectDirectly(Objects.get(m));
                        if (Objects.get(m) instanceof Neuron)
                            NeuronCount++;
                    }

                    ObjectStorage.UnSetPartOfMultiSelectionForAllObjects();

                    for (int m = 0; m < Objects.size(); m++)
                        Objects.get(m).SetPartOfMultiSelection(true);

                    GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText(NeuronCount + " Neurons generated", true, false, false, false);
                }
                else
                {
                    Tools.ShowMessageDialog(null, "Error: out of memory!\r\n(CADU would be too large to be created,\r\nwait a moment until Java freed memory and then\r\ntry smaller counts/succession lengths)");
                }

                GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText(GeneratingMessage);
                GUIElements.GetMainWindow().DoRedraw();
            }
        }
        // ****************************************************************
        // n (negate JavaCADU)
        if (KeyEventProcessor.IsKeyTyped('n'))
        {
            if (DEFINED(NearObject))
            {
                if (NearObject.IsJavaCADU())
                {
                    ((JavaCADU) NearObject).ToggleIsNegative();

                    GUIElements.GetMainWindow().DoRedraw();
                }
            }
        }

        return;
    }

    @Override
    public void TryProcessUserMouseAction(MainWindow.GUIElements GUIElements, KeyEventProcessor KeyEventProcessor, MouseEventProcessor MouseEventProcessor, ObjectStorage ObjectStorage, IOProgram IOProgram)
    {
        if (!(MouseEventsToBeProcessed(GUIElements)))
            return;

        // ****************************************************************
        if (MouseEventProcessor.IsRightButtonClicked())
        {
            if (DEFINED(SingleSelectedObject))
            {
                if (SingleSelectedObject.IsJavaCADU())
                {
                    ((JavaCADU) SingleSelectedObject).Toggle();

                    GUIElements.GetMainWindow().DoRedraw();
                }
            }
        }

        return;
    }
}
